/***
 * JAVANAISE Implementation
 * JvnServerImpl class
 * Contact: 
 *
 * Authors: 
 */

package jvn;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;
import java.io.IOException;
import java.io.Serializable;



public class JvnCoordImpl 	
extends UnicastRemoteObject 
implements JvnRemoteCoord{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	class ServerObject {
		Vector<JvnRemoteServer> remoteServers;
		JvnObject jvnObject;
		JvnObjectState state;
		Serializable object;

		public ServerObject (Vector<JvnRemoteServer> servers, JvnObject obj) {
			remoteServers = servers;
			jvnObject = obj;
			try {
				state = JvnObjectState.STATE_W;
				object = jvnObject.jvnGetObjectState();
			} catch (JvnException e) {
				e.printStackTrace();
			}
		}

	}

	private Hashtable<Integer, String> idToName;
	private Hashtable<String, ServerObject> nameToObject;
	private int idLastObject;

	/**
	 * Default constructor
	 * @throws JvnException
	 **/
	private JvnCoordImpl() throws Exception {
		// to be completed
		Registry r = LocateRegistry.createRegistry(1099);
		r.bind("Coord", this);
		idToName = new Hashtable<Integer, String>();
		nameToObject = new Hashtable<String, ServerObject>();
		idLastObject = 0;


	}
	/**
	 *  Allocate a NEW JVN object id (usually allocated to a 
	 *  newly created JVN object)
	 * @throws java.rmi.RemoteException,JvnException
	 **/
	public synchronized int jvnGetObjectId()
	throws java.rmi.RemoteException,jvn.JvnException {
		idLastObject++;
		return(idLastObject);
	}

	/**
	 * Associate a symbolic name with a JVN object
	 * @param jon : the JVN object name
	 * @param jo  : the JVN object 
	 * @param joi : the JVN object identification
	 * @param js  : the remote reference of the JVNServer
	 * @throws java.rmi.RemoteException,JvnException
	 **/
	public synchronized void jvnRegisterObject(String jon, JvnObject jo, JvnRemoteServer js)
	throws java.rmi.RemoteException,jvn.JvnException{
		idToName.put(jo.jvnGetObjectId(), jon);
		Vector<JvnRemoteServer> serv = new Vector<JvnRemoteServer>();
		serv.add(js);
		ServerObject servAndObject = new ServerObject(serv,jo);
		nameToObject.put(jon, servAndObject);  
	}

	/**
	 * Get the reference of a JVN object managed by a given JVN server 
	 * @param jon : the JVN object name
	 * @param js : the remote reference of the JVNServer
	 * @throws java.rmi.RemoteException,JvnException
	 **/
	public synchronized JvnObject jvnLookupObject(String jon, JvnRemoteServer js)
	throws java.rmi.RemoteException,jvn.JvnException{
		if (nameToObject.containsKey(jon))
			return nameToObject.get(jon).jvnObject;
		else 
			return null;
	}

	/**
	 * Get a Read lock on a JVN object managed by a given JVN server 
	 * @param joi : the JVN object identification
	 * @param js  : the remote reference of the server
	 * @return the current JVN object state
	 * @throws java.rmi.RemoteException, JvnException
	 **/
	public synchronized Serializable jvnLockRead(int joi, JvnRemoteServer js)
	throws java.rmi.RemoteException, JvnException{
		ServerObject server = nameToObject.get(idToName.get(joi));
		if( server.state == JvnObjectState.STATE_NL ) {
			server.state = JvnObjectState.STATE_R;
			server.remoteServers.clear();
			server.remoteServers.add(js);
		} else if( server.state == JvnObjectState.STATE_R || server.state == JvnObjectState.STATE_RC) {
			System.out.println("Coord lock read : adding reader ");
			server.remoteServers.add(js);
		} else if( server.state == JvnObjectState.STATE_W || server.state == JvnObjectState.STATE_WC) {
			JvnRemoteServer server1 = server.remoteServers.get(0);
			System.out.println("Coord lock read : writer for reader ");
			server.object = server1.jvnInvalidateWriterForReader(joi);
			server.state = JvnObjectState.STATE_R;
			server.remoteServers.clear();
			if (server1 != js)
				server.remoteServers.add(server1);		
			server.remoteServers.add(js);
		} else {
			System.out.println("Coord lock read : invalid state");
		}
		return server.object;

	}

	/**
	 * Get a Write lock on a JVN object managed by a given JVN server 
	 * @param joi : the JVN object identification
	 * @param js  : the remote reference of the server
	 * @return the current JVN object state
	 * @throws java.rmi.RemoteException, JvnException
	 **/
	public synchronized Serializable jvnLockWrite(int joi, JvnRemoteServer js)
	throws java.rmi.RemoteException, JvnException{
		ServerObject server = nameToObject.get(idToName.get(joi));
		System.out.println("Coord lock write : Lock State " + server.state);
		if( server.state == JvnObjectState.STATE_R || server.state == JvnObjectState.STATE_RC ) {
			System.out.println("Coord lock write : invalidate reader " + server.remoteServers.size());
			for (int i = 0;i < server.remoteServers.size();i++) {
				System.out.println("Coord lock write : invalidate reader " + i);
				server.remoteServers.get(i).jvnInvalidateReader(joi);
			}
			server.state = JvnObjectState.STATE_W;
		} else if( server.state == JvnObjectState.STATE_W || server.state == JvnObjectState.STATE_WC) {
			JvnRemoteServer server1 = server.remoteServers.get(0);
			System.out.println("Coord lock write : invalidate writer");
			server.object = server1.jvnInvalidateWriter(joi);
		}
		server.remoteServers.clear();
		server.remoteServers.add(js);
		server.state = JvnObjectState.STATE_W;

		return server.object;

	}

	/**
	 * A JVN server terminates
	 * @param js  : the remote reference of the server
	 * @throws java.rmi.RemoteException, JvnException
	 **/
	public void jvnTerminate(JvnRemoteServer js)
	throws java.rmi.RemoteException, JvnException {
		// supprimer tous les locks que le serveur pourrait avoir
	}

	public static void main(String argv[]) throws Exception {

		JvnRemoteCoord jo = new JvnCoordImpl();

	}

}


